package cn.iocoder.yudao.framework.datasource.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.datasource.local.TenantLocalContext;
import cn.iocoder.yudao.framework.datasource.local.ThreadLocalContext;
import cn.iocoder.yudao.framework.datasource.properties.TenantProperties;
import cn.iocoder.yudao.framework.mybatis.interceptor.CustomTenantLineInnerInterceptor;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.schema.Column;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * TODO
 *
 * @className: TenantConfiguration
 * @author: tangkh
 * @date: 2023-09-19 17:25
 */
@ConditionalOnProperty(
        name = {"tenant.enabled"},
        havingValue = "true"
)
@Configuration
@EnableConfigurationProperties({TenantProperties.class})
public class TenantConfiguration {
    @Autowired
    private final TenantProperties tenantProperties;

    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
        TenantLineHandler tenantLineHandler = new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                if (this.ignoreTable(null)) {
                    return null;
                } else {
                    Long tenantId = TenantLocalContext.getTenantId();
                    if (null == tenantId) {
                        throw new RuntimeException("#1129 context.getTenantId() error.");
                    } else {
                        return new LongValue(tenantId);
                    }
                }
            }

            @Override
            public String getTenantIdColumn() {
                return TenantConfiguration.this.tenantProperties.getColumn();
            }

            @Override
            public boolean ignoreTable(String tableName) {
                if (CollectionUtil.isEmpty(TenantConfiguration.this.tenantProperties.getOnlyIncludes()) && CollectionUtil.isNotEmpty(TenantConfiguration.this.tenantProperties.getExcludes())) {
                    Object requestURI = ThreadLocalContext.getAttachment("requestURI");
                    if (requestURI != null && StringUtils.isNotBlank(requestURI.toString())) {
                        boolean flag = TenantConfiguration.this.tenantProperties.getExcludes().stream().anyMatch((item) -> requestURI.toString().contains(item));
                        if (flag) {
                            return flag;
                        }
                    }
                }

                return StringUtils.isBlank(tableName) ? false : TenantConfiguration.this.tenantProperties.getIgnoreTables().stream().anyMatch((t) -> t.equalsIgnoreCase(tableName));
            }

            @Override
            public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
                return TenantConfiguration.this.tenantProperties.getIgnoreSqls().stream().anyMatch((t) -> t.equalsIgnoreCase(tenantIdColumn));
            }
        };
        return new CustomTenantLineInnerInterceptor(tenantLineHandler, this.tenantProperties);
    }

    public TenantConfiguration(TenantProperties tenantProperties) {
        this.tenantProperties = tenantProperties;
    }
}
